library(tidyverse)
library(RPostgreSQL)
library(ggvis)
library(plotly)
library(ggthemes)
library(reshape)
library(htmlwidgets)
library(odbc)
cn1 <- dbConnect(odbc::odbc(),dsn="RDSN")
##Nse indices analysis and visualization
sec_indices <- read.csv("E:/MarketData/NSE_Secotral_Indices/downloadPath.txt", stringsAsFactors = F)
sec_indices <- sec_indices$index
sec_indices_data <- dbGetQuery(cn1, 'select index_name,index_date,change,closing_index_value from nse_indices')
sec_indices_data <- sec_indices_data[which(sec_indices_data$index_name %in% sec_indices),]
sec_ind_trend <- sec_indices_data%>%select(index_name,index_date,change)%>%
  group_by(index_name)%>%
  arrange(index_date)%>%
  mutate(cum_change = cumsum(change))%>%
  filter(change < quantile(change,0.99999))%>%
  filter(change > quantile(change,0.00001))%>%
  select(index_name,index_date,change,cum_change)
stats_sec_indices <- sec_ind_trend%>%filter(index_date >= "2017-01-01")%>%
  group_by(index_name)%>%
  summarise(q1=quantile(change,.25), mean=mean(change),meadin=median(change),q3=quantile(change,0.75), std = sd(change))
## %Chnage trend of each index
sec_chart <- ggplot(sec_ind_trend, aes(x=index_date, y=cum_change, color=index_name))+
  geom_line()+
  facet_wrap("index_name")+
  geom_hline(yintercept=0,color="red", linetype="dotted", size=1)+
  scale_x_date(date_breaks = "6 months", date_labels = "%b %y")+
  xlab("")+
  ylab("")+
  theme(plot.title = element_text(margin = margin(t = 0,b = 100,l = 0,r = 0,unit = "pt")))+
  theme(axis.text.x = element_text(angle = -30))+
  theme(legend.position = "none")+
  ggtitle("Nifty Sectoral Indices - Trend (% Change)")
ggplotly(sec_chart,height = 500,width = 900)

## Visualizing Standard deviation distribution
sd_plot_indices <- ggplot(data=sec_ind_trend, aes(x=index_name, y=change, fill=index_name))+
  geom_violin()+
  stat_summary(fun.y=sd, geom = "point", shape=3, size=2, fill="black")+
  theme(axis.text.x = element_text(angle = -30))+
  geom_hline(yintercept = 0, color="red", linetype="dotted", size=1)+
  theme(legend.position = "none")+
  ggtitle("%Change distribution with Standard deviation")
ggplotly(sd_plot_indices,height = 500,width = 900)

sd_plot2_indices<-ggplot(data=stats_sec_indices, aes(x=reorder(index_name,std), y=std, fill=index_name))+
  geom_bar(stat="identity")+
  coord_flip()+
  ggtitle("Index and Standard Deviation")
ggplotly(sd_plot2_indices,height = 500,width = 900)

sd_plot3_indices_cum <- ggplot(data=sec_ind_trend, aes(x=index_name, y=cum_change, fill=index_name))+
  geom_violin()+
  stat_summary(fun.y=sd, geom="point", shape=3, size=2, fill="black")+
  theme(axis.text.x = element_text(angle=-30))+
  geom_hline(yintercept = 0, color="red",linetype="dotted", size=0.6)+
  ggtitle("Cumulative %Change distribution with Standard deviation")
ggplotly(sd_plot3_indices_cum,height = 500,width = 900)

sd_plot4_indices <- ggplot(data=sec_ind_trend, aes(x=index_name, y=change))+
  geom_boxplot(aes(fill=index_name))+
  stat_summary(fun.y=sd, geom="point", shape=5, size=2)+
  stat_summary(fun.y=mean, geom="point",shape=1,size=2)+
  theme(legend.position = "none")+
  theme(axis.text.x = element_text(angle = -30))+
  xlab("")+
  ylab("")+
  ggtitle("Indices Stats")
ggplotly(sd_plot4_indices,height = 700,width = 1100)

#Datewise sector weight from SQL
data <- dbGetQuery(cn1, 'select nse.trade_date, industryclass.sector_name, sum(nse.close) as sector_value from nse
                   inner join scrips on nse.isin = scrips.isin
                   left join industryclass on scrips.industry = industryclass.industry_subgroup
                   where nse.trade_date >= \'2017-04-01\' and industryclass.sector_name is not NULL
                   group by nse.trade_date, industryclass.sector_name
                   order by industryclass.sector_name, nse.trade_date')
#Visualize sctorwise trend
p1 <- ggplot(data, aes(x=trade_date, y=sector_value))+
  geom_line()+
  geom_smooth(method = 'auto')+
  facet_wrap('sector_name', scales = "free_y",dir = 'h', ncol = 3)+
  theme(strip.text = element_text(size=8, face = "bold"))+
  xlab("")+
  ylab("")+
  scale_x_date(date_breaks = "3 months", date_labels = "%b %y")+
  theme(axis.text.x = element_text(angle = -90, size = 7))+
  theme(axis.text.y = element_text(size = 7))+
  theme(panel.spacing = unit(10,"points"))+
  theme(strip.text = element_text(size = 7))+
  ggtitle('Sector weight ~ Date')
ggplotly(p1, height = 700, width = 1100)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

#Datewise secctor-Industry weight from SQL
data2 <- dbGetQuery(cn1, 'select nse.trade_date, industryclass.sector_name, industryclass.industry_name,sum(nse.close) as industry_value from nse
                    inner join scrips on nse.isin = scrips.isin
                    left join industryclass on scrips.industry = industryclass.industry_subgroup
                    where nse.trade_date >= \'2017-04-01\' and industryclass.sector_name is not NULL
                    group by nse.trade_date, industryclass.sector_name, industryclass.industry_name
                    order by industryclass.sector_name, nse.trade_date')
#Visualize industrywise trend
p2<- ggplot(data2, aes(x=trade_date, y=industry_value))+
  geom_line()+
  geom_smooth(method = 'auto')+
  facet_wrap('industry_name', scales = "free_y",dir = 'h', ncol = 4)+
  theme(strip.text = element_text(size=8, face = "bold"))+
  xlab("")+
  ylab("")+
  scale_x_date(date_breaks = "3 months", date_labels = "%b %y")+
  theme(axis.text.x = element_text(size = 7, angle = -30))+
  theme(axis.text.y = element_text(size = 7))+
  ggtitle('Industry weight ~ Date')
ggplotly(p2, height = 1200,width = 1100)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

#Check on stocks specific to industry or sector. Change query to realign data
data3 <- dbGetQuery(cn1, 'select nse.trade_date, industryclass.sector_name, industryclass.industry_name,nse.symbol, nse.close from nse
                    inner join scrips on nse.isin = scrips.isin
                    left join industryclass on scrips.industry = industryclass.industry_subgroup
                    where industryclass.industry_name = \'Retailing\' and nse.trade_date >= \'2017-04-01\' and industryclass.sector_name is not NULL
                    group by nse.trade_date, industryclass.sector_name, industryclass.industry_name,nse.symbol, nse.close
                    order by nse.symbol,nse.trade_date')
p3 <- ggplot(data3, aes(x=trade_date, y=close))+
  geom_line()+
  geom_smooth(method = 'auto')+
  facet_wrap('symbol', scales = "free_y", ncol = 4)+
  xlab("")+
  ylab("")+
  scale_x_date(date_breaks = "3 months", date_labels = "%b %y")+
  theme(axis.text.x = element_text(size = 8, angle = -30))+
  theme(axis.text.y = element_text(size = 8))+
  theme(panel.spacing = unit(0.2,"cm"))+
  ggtitle(paste(data3$industry_name[1]," Sector stocks performace"))
ggplotly(p3, height = 700, width = 1100)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

#portfoliodata visualization
data4 <- dbGetQuery(cn1, 'select nse.trade_date, demat.scrip_code, demat.hold_value, (demat.dp_bal * nse.close) as currentValue, ROUND(((demat.dp_bal * nse.close) - demat.hold_value)::numeric,2) as profit, ROUND((((demat.dp_bal * nse.close) - demat.hold_value)/demat.hold_value * 100)::numeric,2) as profit_percent from demat
                    inner join nse on demat.isin = nse.isin
                    where nse.trade_date >= \'2017-09-01\'
                    group by demat.scrip_code, nse.trade_date, demat.hold_value, demat.dp_bal, nse.close
                    order by nse.trade_date desc, profit')
p4 <- ggplot(data4, aes(x=trade_date, y=profit_percent))+
  geom_line()+
  geom_smooth(method = 'auto')+
  geom_hline(yintercept = 0, size = 0.3, linetype="dotted", color="red")+
  facet_wrap('scrip_code', scales = "free_y",ncol = 5)+
  xlab("")+
  ylab("")+
  scale_x_date(date_breaks = "3 months", date_labels = "%b %y")+
  theme(axis.text.x = element_text(size = 8, angle = -30))+
  theme(axis.text.y = element_text(size = 8))+
  ggtitle('Portfolio stocks - Datewise Profit Percent Trend')
ggplotly(p4, height = 700, width = 1000)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

portfolio_gains <- dbGetQuery(cn1, "select nse.trade_date, nse.symbol, nse.close, sum(demat.hold_value)as investedvalue,(demat.dp_bal * nse.close) as currentvalue,
ROUND(((demat.dp_bal * nse.close) - lag((demat.dp_bal * nse.close)) over(order by symbol, trade_date))::numeric,2) as days_gain,
                              ROUND((((demat.dp_bal * nse.close) - lag((demat.dp_bal * nse.close)) over(order by symbol, trade_date))/lag((demat.dp_bal * nse.close)) over(order by symbol, trade_date))::numeric,2) as days_gain_percent,
                              ROUND(((demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2) as overallgain,
                              ROUND((((demat.dp_bal * nse.close) - sum(demat.hold_value))/sum(demat.hold_value)*100)::numeric,2) as overallgain_percent,
                              nse_indices.closing_index_value as Nifty,
                              ROUND((nse_indices.closing_index_value - lag(nse_indices.closing_index_value) over (order by symbol, trade_date))::numeric,2) as niftychange,
                              ROUND((((nse_indices.closing_index_value) - lag(nse_indices.closing_index_value) over (order by symbol, trade_date))/(lag(nse_indices.closing_index_value) over (order by symbol, trade_date))*100)::numeric,2) as niftychange_percent
                              from nse
                              inner join demat on demat.isin = nse.isin
                              left join nse_indices on nse_indices.index_date = nse.trade_date
                              where nse_indices.index_name = 'Nifty 50' and nse.trade_date >= \'2017-09-01\'
                              group by nse.trade_date, nse.symbol, nse.close, demat.dp_bal,nse_indices.closing_index_value
                              order by nse.trade_date desc, nse.symbol")
portfolio_gains <- drop_na(portfolio_gains)
# for(i in seq_along(portfolio_gains$days_gain))
# {
#   if(portfolio_gains$days_gain[i] <= 0){
#     portfolio_gains$growthType[i] <- "Negative"
#   }else{
#     portfolio_gains$growthType[i] <- "Positive"
#   }
# }
portfolio_gains$growthType <- sapply(portfolio_gains$days_gain, function(x) if(x<=0){"Negative"}else{"Positive"})
pos_neg <- portfolio_gains%>%select(symbol, growthType)%>%
  group_by(symbol,growthType)%>%
  summarise(cnt=n())%>%
  arrange(growthType,-cnt)
pos_neg_p <- ggplot(pos_neg[which(pos_neg$growthType=="Negative"),], aes(x=reorder(symbol, cnt), y=cnt, fill="lightred"))+
  geom_bar(stat="identity")+
  coord_flip()+
  theme(legend.position = "none")+
  ggtitle("No. of days Stock gave negative returns")
ggplotly(pos_neg_p, height = 500, width = 900)

## Lets plot violin with std and mean
##calculate positive negative drag
p_n_drag <- portfolio_gains%>%select(symbol, days_gain_percent, growthType)%>%
  group_by(symbol,growthType)%>%
  arrange(desc(days_gain_percent))%>%
  slice(2:n())%>%   ## dropping first row as it might contain outliers due to non availability of data on earlier dates
  summarise(total=sum(days_gain_percent))
  
    
p_n_drag$total <- abs(p_n_drag$total)
p_n_drag <- p_n_drag%>%arrange(desc(growthType), desc(total))
p_n_drag_chart <- ggplot(p_n_drag, aes(x=reorder(symbol, -total), y=total, fill=growthType))+
  geom_bar(stat="identity", position = "fill")+
  coord_flip()+
  labs(x="",y="",title="Positive and Negative Drag")
ggplotly(p_n_drag_chart, height = 600, width = 1100)

## Check culprits dragging portfolio even when nifty change is positive
culprit_stocks <- portfolio_gains[which(portfolio_gains$days_gain < 0 & portfolio_gains$niftychange > 0),] 
culprit_stocks_stats <- culprit_stocks%>%select(symbol, days_gain_percent, overallgain)%>%
  group_by(symbol)%>%
  summarise(times_defaulted_nifty=n(),lossPercent_during_default=sum(days_gain_percent), loss_during_default = sum(overallgain[1]))
ggplot(culprit_stocks, aes(x=symbol))+
  geom_bar()+
  coord_flip()
## Daily portfolio value, profit % and Nifty 50 closing index
data5 <- dbGetQuery(cn1, 'select nse.trade_date, sum(demat.dp_bal * nse.close) as portfolioValue, ROUND((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2) as profit, ROUND(((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))/sum(demat.hold_value)*100)::numeric,2) as profit_percent, nse_indices.closing_index_value from demat
                    inner join nse on demat.isin = nse.isin
                    left join nse_indices on nse_indices.index_date = nse.trade_date
                    where nse_indices.index_name = \'Nifty 50\' and nse.trade_date >= \'2017-08-01\'
                    group by nse.trade_date, nse_indices.index_name, nse_indices.closing_index_value
                    order by nse.trade_date desc')
ggplot(data5, aes(x=trade_date, y=profit_percent))+
  geom_line()+
  geom_smooth(method = 'auto')+
  geom_hline(yintercept = 0, color = 'red', linetype='dashed')+
  geom_line(aes(y=closing_index_value/1000), color='blue')+
  ggtitle('Portfolio Profit % ~ Nifty Trend')
#Check portfolio days gain % against Nifty gain %
data6 <- dbGetQuery(cn1, 'select nse.trade_date, sum(demat.dp_bal * nse.close) as portfolioValue,(ROUND((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2)) - lag(ROUND((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2)) over (order by trade_date) as days_gain,ROUND(((ROUND((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2)) - lag(ROUND((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2)) over (order by trade_date))*100/(sum(demat.hold_value))::numeric,2) as days_gain_percent,ROUND((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))::numeric,2) as overall_gain,ROUND(((sum(demat.dp_bal * nse.close) - sum(demat.hold_value))/sum(demat.hold_value)*100)::numeric,2) as overall_gain_percent, nse_indices.closing_index_value as nifty, ROUND((((nse_indices.closing_index_value - lag(nse_indices.closing_index_value) over (order by nse.trade_date))/nse_indices.closing_index_value)*100)::numeric,2) as nifty_change_percent  from demat
                    inner join nse on demat.isin = nse.isin
                    left join nse_indices on nse_indices.index_date = nse.trade_date
                    group by nse.trade_date, nse_indices.index_name, nse_indices.closing_index_value
                    having nse_indices.index_name = \'Nifty 50\' and nse.trade_date > \'2017-08-01\'
                    order by nse.trade_date desc')
data6[is.na(data6)]<- 0
sub1_data6 <- data6[,c(1,4,8)]
sub1_data6 <- sub1_data6%>%
  arrange(trade_date)%>%
  mutate(Portfolio_Gain_Percent = cumsum(days_gain_percent), Nifty_Gain_Percent = cumsum(nifty_change_percent))%>%
  select(trade_date, Portfolio_Gain_Percent, Nifty_Gain_Percent)
sub1_data6 <- melt(sub1_data6, id=c("trade_date"))
p6<-ggplot(sub1_data6, aes(x=trade_date, y=value, color=variable))+
  geom_line(size=0.7)+
  xlab("Trade Date")+
  ylab("Gain %")+
  scale_x_date(date_breaks = "1 month", date_labels = "%b %y")+
  ggtitle("Daily Portfolio Gain % ~ Nifty Change %")+
  theme_economist()
ggplotly(p6, width = 1000, height = 400)

### Analysing current trends
nse <- dbGetQuery(cn1,'select nse.trade_date, nse.symbol, nse.isin, nse.close, industryclass.sector_name, industryclass.industry_name  from nse
                  left join scrips on nse.isin = scrips.isin
                  left join industryclass on industryclass.industry_subgroup = scrips.industry
                  group by nse.symbol, nse.isin, nse.trade_date, nse.close, industryclass.sector_name, industryclass.industry_name
                  order by nse.trade_date desc, nse.symbol')
nse_days <- dbGetQuery(cn1,'select trade_date from nse group by trade_date order by trade_date desc')
d1 <- nse[nse$trade_date %in% nse_days[1,1],]
d60 <- nse[nse$trade_date %in% nse_days[60,1],]
d120 <- nse[nse$trade_date %in% nse_days[120,1],]
d180 <- nse[nse$trade_date %in% nse_days[180,1],]
d240 <- nse[nse$trade_date %in% nse_days[240,1],]
d300 <- nse[nse$trade_date %in% nse_days[300,1],]
d360 <- nse[nse$trade_date %in% nse_days[360,1],]
trend_data <- d1
trend_data$d60 <- d60[match(trend_data$isin, d60$isin),'close']
trend_data$d120 <- d120[match(trend_data$isin, d120$isin),'close']
trend_data$d180 <- d180[match(trend_data$isin, d180$isin),'close']
trend_data$d240 <- d240[match(trend_data$isin, d240$isin),'close']
trend_data$d300 <- d300[match(trend_data$isin, d300$isin),'close']
trend_data$d360 <- d360[match(trend_data$isin, d360$isin),'close']
trend_data <- trend_data[complete.cases(trend_data),]
trend_data$d60_prct <- round((trend_data$close - trend_data$d60)/trend_data$close * 100,2)
trend_data$d120_prct <- round((trend_data$close - trend_data$d120)/trend_data$close * 100,2)
trend_data$d180_prct <- round((trend_data$close - trend_data$d180)/trend_data$close * 100,2)
trend_data$d240_prct <- round((trend_data$close - trend_data$d240)/trend_data$close * 100,2)
trend_data$d300_prct <- round((trend_data$close - trend_data$d300)/trend_data$close * 100,2)
trend_data$d360_prct <- round((trend_data$close - trend_data$d360)/trend_data$close * 100,2)
d360_top10 <- top_n(trend_data, 10, wt = trend_data$d360_prct)
d60_top10 <- top_n(trend_data, 10, wt = trend_data$d60_prct)
p7 <- ggplot(d360_top10, aes(x=reorder(symbol,d360_prct), y=d360_prct, fill=symbol))+
  geom_col()+
  coord_flip()+
  ggtitle("Top 10 Performers - Long Term 360+ Trades")+
  xlab("")+
  ylab("")
ggplotly(p7, height = 400, width = 800)

LT_Top10_Trend <- ggplot(nse[nse$isin %in% d360_top10$isin,], aes(x=trade_date, y=close))+
  geom_line()+
  geom_smooth(method = 'auto')+
  xlab("")+
  ylab("")+
  facet_wrap('symbol', scales = "free_y")+
  scale_x_date(date_breaks = "3 months", date_labels = "%b %y")+
  theme(axis.text.x = element_text(angle = -30))+
  ggtitle("TREND - Top 10 Performers - Long Term 360+ Trades")
ggplotly(LT_Top10_Trend, width=1000, height=600)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

ST_top10_p <- ggplot(d60_top10, aes(x=reorder(symbol,d60_prct), y=d60_prct, fill=symbol))+
  geom_col()+
  coord_flip()+
  ggtitle("Top 10 Performers - Short Term 60+ Trades")+
  xlab("")+
  ylab("")
ggplotly(ST_top10_p, height = 400, width = 800)

ST_top10_p_trend <- ggplot(nse[nse$isin %in% d60_top10$isin,], aes(x=trade_date, y=close))+
  geom_line()+
  geom_smooth(method = 'auto')+
  facet_wrap('symbol', scales = "free_y")+
  scale_x_date(date_breaks = "3 months", date_labels = "%b %y")+
  theme(axis.text.x = element_text(angle = -30))+
  ggtitle('TREND - TOP 10 Short Term 60+ trades')
ggplotly(ST_top10_p_trend, width=1000, height=600)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

industries_d60_top5 <- trend_data %>% 
  arrange(industry_name, desc(d60_prct)) %>%
  group_by(industry_name) %>%
  top_n(n=5,wt = d60_prct) # %>%
#filter(row_number() <=5)
p_short_top5 <- ggplot(industries_d60_top5, aes(x=reorder(symbol,d60_prct), y=d60_prct))+
  geom_col()+
  coord_flip()+
  facet_wrap('industry_name', scales = 'free_y', ncol = 4)+
  xlab("")+
  ylab("")+
  theme(strip.text = element_text(size = 6, face = "bold"))+
  theme(axis.text.y = element_text(size = 6))+
  theme(panel.spacing.y = unit(0.5,"lines"))+
  theme(panel.spacing.x = unit(0.5,"lines"))+
  ggtitle('Short Term 60+ trades toppers in % gain')
ggplotly(p_short_top5, height = 800, width = 1000)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KFJQb3N0Z3JlU1FMKQ0KbGlicmFyeShnZ3ZpcykNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkocmVzaGFwZSkNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQpsaWJyYXJ5KG9kYmMpDQoNCg0KY24xIDwtIGRiQ29ubmVjdChvZGJjOjpvZGJjKCksZHNuPSJSRFNOIikNCg0KDQojI05zZSBpbmRpY2VzIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uDQoNCnNlY19pbmRpY2VzIDwtIHJlYWQuY3N2KCJFOi9NYXJrZXREYXRhL05TRV9TZWNvdHJhbF9JbmRpY2VzL2Rvd25sb2FkUGF0aC50eHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRikNCnNlY19pbmRpY2VzIDwtIHNlY19pbmRpY2VzJGluZGV4DQoNCnNlY19pbmRpY2VzX2RhdGEgPC0gZGJHZXRRdWVyeShjbjEsICdzZWxlY3QgaW5kZXhfbmFtZSxpbmRleF9kYXRlLGNoYW5nZSxjbG9zaW5nX2luZGV4X3ZhbHVlIGZyb20gbnNlX2luZGljZXMnKQ0Kc2VjX2luZGljZXNfZGF0YSA8LSBzZWNfaW5kaWNlc19kYXRhW3doaWNoKHNlY19pbmRpY2VzX2RhdGEkaW5kZXhfbmFtZSAlaW4lIHNlY19pbmRpY2VzKSxdDQoNCnNlY19pbmRfdHJlbmQgPC0gc2VjX2luZGljZXNfZGF0YSU+JXNlbGVjdChpbmRleF9uYW1lLGluZGV4X2RhdGUsY2hhbmdlKSU+JQ0KICBncm91cF9ieShpbmRleF9uYW1lKSU+JQ0KICBhcnJhbmdlKGluZGV4X2RhdGUpJT4lDQogIG11dGF0ZShjdW1fY2hhbmdlID0gY3Vtc3VtKGNoYW5nZSkpJT4lDQogIGZpbHRlcihjaGFuZ2UgPCBxdWFudGlsZShjaGFuZ2UsMC45OTk5OSkpJT4lDQogIGZpbHRlcihjaGFuZ2UgPiBxdWFudGlsZShjaGFuZ2UsMC4wMDAwMSkpJT4lDQogIHNlbGVjdChpbmRleF9uYW1lLGluZGV4X2RhdGUsY2hhbmdlLGN1bV9jaGFuZ2UpDQoNCnN0YXRzX3NlY19pbmRpY2VzIDwtIHNlY19pbmRfdHJlbmQlPiVmaWx0ZXIoaW5kZXhfZGF0ZSA+PSAiMjAxNy0wMS0wMSIpJT4lDQogIGdyb3VwX2J5KGluZGV4X25hbWUpJT4lDQogIHN1bW1hcmlzZShxMT1xdWFudGlsZShjaGFuZ2UsLjI1KSwgbWVhbj1tZWFuKGNoYW5nZSksbWVhZGluPW1lZGlhbihjaGFuZ2UpLHEzPXF1YW50aWxlKGNoYW5nZSwwLjc1KSwgc3RkID0gc2QoY2hhbmdlKSkNCg0KDQojIyAlQ2huYWdlIHRyZW5kIG9mIGVhY2ggaW5kZXgNCnNlY19jaGFydCA8LSBnZ3Bsb3Qoc2VjX2luZF90cmVuZCwgYWVzKHg9aW5kZXhfZGF0ZSwgeT1jdW1fY2hhbmdlLCBjb2xvcj1pbmRleF9uYW1lKSkrDQogIGdlb21fbGluZSgpKw0KICBmYWNldF93cmFwKCJpbmRleF9uYW1lIikrDQogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLGNvbG9yPSJyZWQiLCBsaW5ldHlwZT0iZG90dGVkIiwgc2l6ZT0xKSsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjYgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMCxiID0gMTAwLGwgPSAwLHIgPSAwLHVuaXQgPSAicHQiKSkpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IC0zMCkpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKw0KICBnZ3RpdGxlKCJOaWZ0eSBTZWN0b3JhbCBJbmRpY2VzIC0gVHJlbmQgKCUgQ2hhbmdlKSIpDQoNCmdncGxvdGx5KHNlY19jaGFydCxoZWlnaHQgPSA1MDAsd2lkdGggPSA5MDApDQoNCg0KDQoNCiMjIFZpc3VhbGl6aW5nIFN0YW5kYXJkIGRldmlhdGlvbiBkaXN0cmlidXRpb24NCnNkX3Bsb3RfaW5kaWNlcyA8LSBnZ3Bsb3QoZGF0YT1zZWNfaW5kX3RyZW5kLCBhZXMoeD1pbmRleF9uYW1lLCB5PWNoYW5nZSwgZmlsbD1pbmRleF9uYW1lKSkrDQogIGdlb21fdmlvbGluKCkrDQogIHN0YXRfc3VtbWFyeShmdW4ueT1zZCwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlPTMsIHNpemU9MiwgZmlsbD0iYmxhY2siKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAtMzApKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3I9InJlZCIsIGxpbmV0eXBlPSJkb3R0ZWQiLCBzaXplPTEpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKw0KICBnZ3RpdGxlKCIlQ2hhbmdlIGRpc3RyaWJ1dGlvbiB3aXRoIFN0YW5kYXJkIGRldmlhdGlvbiIpDQoNCmdncGxvdGx5KHNkX3Bsb3RfaW5kaWNlcyxoZWlnaHQgPSA1MDAsd2lkdGggPSA5MDApDQoNCnNkX3Bsb3QyX2luZGljZXM8LWdncGxvdChkYXRhPXN0YXRzX3NlY19pbmRpY2VzLCBhZXMoeD1yZW9yZGVyKGluZGV4X25hbWUsc3RkKSwgeT1zdGQsIGZpbGw9aW5kZXhfbmFtZSkpKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKw0KICBjb29yZF9mbGlwKCkrDQogIGdndGl0bGUoIkluZGV4IGFuZCBTdGFuZGFyZCBEZXZpYXRpb24iKQ0KDQpnZ3Bsb3RseShzZF9wbG90Ml9pbmRpY2VzLGhlaWdodCA9IDUwMCx3aWR0aCA9IDkwMCkNCg0KDQpzZF9wbG90M19pbmRpY2VzX2N1bSA8LSBnZ3Bsb3QoZGF0YT1zZWNfaW5kX3RyZW5kLCBhZXMoeD1pbmRleF9uYW1lLCB5PWN1bV9jaGFuZ2UsIGZpbGw9aW5kZXhfbmFtZSkpKw0KICBnZW9tX3Zpb2xpbigpKw0KICBzdGF0X3N1bW1hcnkoZnVuLnk9c2QsIGdlb209InBvaW50Iiwgc2hhcGU9Mywgc2l6ZT0yLCBmaWxsPSJibGFjayIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0tMzApKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3I9InJlZCIsbGluZXR5cGU9ImRvdHRlZCIsIHNpemU9MC42KSsNCiAgZ2d0aXRsZSgiQ3VtdWxhdGl2ZSAlQ2hhbmdlIGRpc3RyaWJ1dGlvbiB3aXRoIFN0YW5kYXJkIGRldmlhdGlvbiIpDQoNCmdncGxvdGx5KHNkX3Bsb3QzX2luZGljZXNfY3VtLGhlaWdodCA9IDUwMCx3aWR0aCA9IDkwMCkNCg0KDQpzZF9wbG90NF9pbmRpY2VzIDwtIGdncGxvdChkYXRhPXNlY19pbmRfdHJlbmQsIGFlcyh4PWluZGV4X25hbWUsIHk9Y2hhbmdlKSkrDQogIGdlb21fYm94cGxvdChhZXMoZmlsbD1pbmRleF9uYW1lKSkrDQogIHN0YXRfc3VtbWFyeShmdW4ueT1zZCwgZ2VvbT0icG9pbnQiLCBzaGFwZT01LCBzaXplPTIpKw0KICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVhbiwgZ2VvbT0icG9pbnQiLHNoYXBlPTEsc2l6ZT0yKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAtMzApKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpKw0KICBnZ3RpdGxlKCJJbmRpY2VzIFN0YXRzIikNCg0KZ2dwbG90bHkoc2RfcGxvdDRfaW5kaWNlcyxoZWlnaHQgPSA3MDAsd2lkdGggPSAxMTAwKQ0KDQoNCiNEYXRld2lzZSBzZWN0b3Igd2VpZ2h0IGZyb20gU1FMDQoNCmRhdGEgPC0gZGJHZXRRdWVyeShjbjEsICdzZWxlY3QgbnNlLnRyYWRlX2RhdGUsIGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUsIHN1bShuc2UuY2xvc2UpIGFzIHNlY3Rvcl92YWx1ZSBmcm9tIG5zZQ0KICAgICAgICAgICAgICAgICAgIGlubmVyIGpvaW4gc2NyaXBzIG9uIG5zZS5pc2luID0gc2NyaXBzLmlzaW4NCiAgICAgICAgICAgICAgICAgICBsZWZ0IGpvaW4gaW5kdXN0cnljbGFzcyBvbiBzY3JpcHMuaW5kdXN0cnkgPSBpbmR1c3RyeWNsYXNzLmluZHVzdHJ5X3N1Ymdyb3VwDQogICAgICAgICAgICAgICAgICAgd2hlcmUgbnNlLnRyYWRlX2RhdGUgPj0gXCcyMDE3LTA0LTAxXCcgYW5kIGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUgaXMgbm90IE5VTEwNCiAgICAgICAgICAgICAgICAgICBncm91cCBieSBuc2UudHJhZGVfZGF0ZSwgaW5kdXN0cnljbGFzcy5zZWN0b3JfbmFtZQ0KICAgICAgICAgICAgICAgICAgIG9yZGVyIGJ5IGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUsIG5zZS50cmFkZV9kYXRlJykNCg0KDQojVmlzdWFsaXplIHNjdG9yd2lzZSB0cmVuZA0KDQpwMSA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHg9dHJhZGVfZGF0ZSwgeT1zZWN0b3JfdmFsdWUpKSsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdhdXRvJykrDQogIGZhY2V0X3dyYXAoJ3NlY3Rvcl9uYW1lJywgc2NhbGVzID0gImZyZWVfeSIsZGlyID0gJ2gnLCBuY29sID0gMykrDQogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04LCBmYWNlID0gImJvbGQiKSkrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKSsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjMgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAtOTAsIHNpemUgPSA3KSkrDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkrDQogIHRoZW1lKHBhbmVsLnNwYWNpbmcgPSB1bml0KDEwLCJwb2ludHMiKSkrDQogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSsNCiAgZ2d0aXRsZSgnU2VjdG9yIHdlaWdodCB+IERhdGUnKQ0KDQoNCmdncGxvdGx5KHAxLCBoZWlnaHQgPSA3MDAsIHdpZHRoID0gMTEwMCkNCg0KDQojRGF0ZXdpc2Ugc2VjY3Rvci1JbmR1c3RyeSB3ZWlnaHQgZnJvbSBTUUwNCg0KZGF0YTIgPC0gZGJHZXRRdWVyeShjbjEsICdzZWxlY3QgbnNlLnRyYWRlX2RhdGUsIGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUsIGluZHVzdHJ5Y2xhc3MuaW5kdXN0cnlfbmFtZSxzdW0obnNlLmNsb3NlKSBhcyBpbmR1c3RyeV92YWx1ZSBmcm9tIG5zZQ0KICAgICAgICAgICAgICAgICAgICBpbm5lciBqb2luIHNjcmlwcyBvbiBuc2UuaXNpbiA9IHNjcmlwcy5pc2luDQogICAgICAgICAgICAgICAgICAgIGxlZnQgam9pbiBpbmR1c3RyeWNsYXNzIG9uIHNjcmlwcy5pbmR1c3RyeSA9IGluZHVzdHJ5Y2xhc3MuaW5kdXN0cnlfc3ViZ3JvdXANCiAgICAgICAgICAgICAgICAgICAgd2hlcmUgbnNlLnRyYWRlX2RhdGUgPj0gXCcyMDE3LTA0LTAxXCcgYW5kIGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUgaXMgbm90IE5VTEwNCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgYnkgbnNlLnRyYWRlX2RhdGUsIGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUsIGluZHVzdHJ5Y2xhc3MuaW5kdXN0cnlfbmFtZQ0KICAgICAgICAgICAgICAgICAgICBvcmRlciBieSBpbmR1c3RyeWNsYXNzLnNlY3Rvcl9uYW1lLCBuc2UudHJhZGVfZGF0ZScpDQoNCiNWaXN1YWxpemUgaW5kdXN0cnl3aXNlIHRyZW5kDQoNCnAyPC0gZ2dwbG90KGRhdGEyLCBhZXMoeD10cmFkZV9kYXRlLCB5PWluZHVzdHJ5X3ZhbHVlKSkrDQogIGdlb21fbGluZSgpKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAnYXV0bycpKw0KICBmYWNldF93cmFwKCdpbmR1c3RyeV9uYW1lJywgc2NhbGVzID0gImZyZWVfeSIsZGlyID0gJ2gnLCBuY29sID0gNCkrDQogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04LCBmYWNlID0gImJvbGQiKSkrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKSsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjMgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcsIGFuZ2xlID0gLTMwKSkrDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkrDQogIGdndGl0bGUoJ0luZHVzdHJ5IHdlaWdodCB+IERhdGUnKQ0KDQpnZ3Bsb3RseShwMiwgaGVpZ2h0ID0gMTIwMCx3aWR0aCA9IDExMDApDQoNCg0KI0NoZWNrIG9uIHN0b2NrcyBzcGVjaWZpYyB0byBpbmR1c3RyeSBvciBzZWN0b3IuIENoYW5nZSBxdWVyeSB0byByZWFsaWduIGRhdGENCg0KZGF0YTMgPC0gZGJHZXRRdWVyeShjbjEsICdzZWxlY3QgbnNlLnRyYWRlX2RhdGUsIGluZHVzdHJ5Y2xhc3Muc2VjdG9yX25hbWUsIGluZHVzdHJ5Y2xhc3MuaW5kdXN0cnlfbmFtZSxuc2Uuc3ltYm9sLCBuc2UuY2xvc2UgZnJvbSBuc2UNCiAgICAgICAgICAgICAgICAgICAgaW5uZXIgam9pbiBzY3JpcHMgb24gbnNlLmlzaW4gPSBzY3JpcHMuaXNpbg0KICAgICAgICAgICAgICAgICAgICBsZWZ0IGpvaW4gaW5kdXN0cnljbGFzcyBvbiBzY3JpcHMuaW5kdXN0cnkgPSBpbmR1c3RyeWNsYXNzLmluZHVzdHJ5X3N1Ymdyb3VwDQogICAgICAgICAgICAgICAgICAgIHdoZXJlIGluZHVzdHJ5Y2xhc3MuaW5kdXN0cnlfbmFtZSA9IFwnUmV0YWlsaW5nXCcgYW5kIG5zZS50cmFkZV9kYXRlID49IFwnMjAxNy0wNC0wMVwnIGFuZCBpbmR1c3RyeWNsYXNzLnNlY3Rvcl9uYW1lIGlzIG5vdCBOVUxMDQogICAgICAgICAgICAgICAgICAgIGdyb3VwIGJ5IG5zZS50cmFkZV9kYXRlLCBpbmR1c3RyeWNsYXNzLnNlY3Rvcl9uYW1lLCBpbmR1c3RyeWNsYXNzLmluZHVzdHJ5X25hbWUsbnNlLnN5bWJvbCwgbnNlLmNsb3NlDQogICAgICAgICAgICAgICAgICAgIG9yZGVyIGJ5IG5zZS5zeW1ib2wsbnNlLnRyYWRlX2RhdGUnKQ0KDQpwMyA8LSBnZ3Bsb3QoZGF0YTMsIGFlcyh4PXRyYWRlX2RhdGUsIHk9Y2xvc2UpKSsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdhdXRvJykrDQogIGZhY2V0X3dyYXAoJ3N5bWJvbCcsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gNCkrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKSsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjMgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGFuZ2xlID0gLTMwKSkrDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkrDQogIHRoZW1lKHBhbmVsLnNwYWNpbmcgPSB1bml0KDAuMiwiY20iKSkrDQogIGdndGl0bGUocGFzdGUoZGF0YTMkaW5kdXN0cnlfbmFtZVsxXSwiIFNlY3RvciBzdG9ja3MgcGVyZm9ybWFjZSIpKQ0KDQpnZ3Bsb3RseShwMywgaGVpZ2h0ID0gNzAwLCB3aWR0aCA9IDExMDApDQoNCg0KI3BvcnRmb2xpb2RhdGEgdmlzdWFsaXphdGlvbg0KDQpkYXRhNCA8LSBkYkdldFF1ZXJ5KGNuMSwgJ3NlbGVjdCBuc2UudHJhZGVfZGF0ZSwgZGVtYXQuc2NyaXBfY29kZSwgZGVtYXQuaG9sZF92YWx1ZSwgKGRlbWF0LmRwX2JhbCAqIG5zZS5jbG9zZSkgYXMgY3VycmVudFZhbHVlLCBST1VORCgoKGRlbWF0LmRwX2JhbCAqIG5zZS5jbG9zZSkgLSBkZW1hdC5ob2xkX3ZhbHVlKTo6bnVtZXJpYywyKSBhcyBwcm9maXQsIFJPVU5EKCgoKGRlbWF0LmRwX2JhbCAqIG5zZS5jbG9zZSkgLSBkZW1hdC5ob2xkX3ZhbHVlKS9kZW1hdC5ob2xkX3ZhbHVlICogMTAwKTo6bnVtZXJpYywyKSBhcyBwcm9maXRfcGVyY2VudCBmcm9tIGRlbWF0DQogICAgICAgICAgICAgICAgICAgIGlubmVyIGpvaW4gbnNlIG9uIGRlbWF0LmlzaW4gPSBuc2UuaXNpbg0KICAgICAgICAgICAgICAgICAgICB3aGVyZSBuc2UudHJhZGVfZGF0ZSA+PSBcJzIwMTctMDktMDFcJw0KICAgICAgICAgICAgICAgICAgICBncm91cCBieSBkZW1hdC5zY3JpcF9jb2RlLCBuc2UudHJhZGVfZGF0ZSwgZGVtYXQuaG9sZF92YWx1ZSwgZGVtYXQuZHBfYmFsLCBuc2UuY2xvc2UNCiAgICAgICAgICAgICAgICAgICAgb3JkZXIgYnkgbnNlLnRyYWRlX2RhdGUgZGVzYywgcHJvZml0JykNCg0KcDQgPC0gZ2dwbG90KGRhdGE0LCBhZXMoeD10cmFkZV9kYXRlLCB5PXByb2ZpdF9wZXJjZW50KSkrDQogIGdlb21fbGluZSgpKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAnYXV0bycpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBzaXplID0gMC4zLCBsaW5ldHlwZT0iZG90dGVkIiwgY29sb3I9InJlZCIpKw0KICBmYWNldF93cmFwKCdzY3JpcF9jb2RlJywgc2NhbGVzID0gImZyZWVfeSIsbmNvbCA9IDUpKw0KICB4bGFiKCIiKSsNCiAgeWxhYigiIikrDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiViICV5IikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBhbmdsZSA9IC0zMCkpKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpKw0KICBnZ3RpdGxlKCdQb3J0Zm9saW8gc3RvY2tzIC0gRGF0ZXdpc2UgUHJvZml0IFBlcmNlbnQgVHJlbmQnKQ0KDQpnZ3Bsb3RseShwNCwgaGVpZ2h0ID0gNzAwLCB3aWR0aCA9IDEwMDApDQoNCnBvcnRmb2xpb19nYWlucyA8LSBkYkdldFF1ZXJ5KGNuMSwgInNlbGVjdCBuc2UudHJhZGVfZGF0ZSwgbnNlLnN5bWJvbCwgbnNlLmNsb3NlLCBzdW0oZGVtYXQuaG9sZF92YWx1ZSlhcyBpbnZlc3RlZHZhbHVlLChkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIGFzIGN1cnJlbnR2YWx1ZSwNClJPVU5EKCgoZGVtYXQuZHBfYmFsICogbnNlLmNsb3NlKSAtIGxhZygoZGVtYXQuZHBfYmFsICogbnNlLmNsb3NlKSkgb3ZlcihvcmRlciBieSBzeW1ib2wsIHRyYWRlX2RhdGUpKTo6bnVtZXJpYywyKSBhcyBkYXlzX2dhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBST1VORCgoKChkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gbGFnKChkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpKSBvdmVyKG9yZGVyIGJ5IHN5bWJvbCwgdHJhZGVfZGF0ZSkpL2xhZygoZGVtYXQuZHBfYmFsICogbnNlLmNsb3NlKSkgb3ZlcihvcmRlciBieSBzeW1ib2wsIHRyYWRlX2RhdGUpKTo6bnVtZXJpYywyKSBhcyBkYXlzX2dhaW5fcGVyY2VudCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJPVU5EKCgoZGVtYXQuZHBfYmFsICogbnNlLmNsb3NlKSAtIHN1bShkZW1hdC5ob2xkX3ZhbHVlKSk6Om51bWVyaWMsMikgYXMgb3ZlcmFsbGdhaW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBST1VORCgoKChkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKS9zdW0oZGVtYXQuaG9sZF92YWx1ZSkqMTAwKTo6bnVtZXJpYywyKSBhcyBvdmVyYWxsZ2Fpbl9wZXJjZW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZSBhcyBOaWZ0eSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJPVU5EKChuc2VfaW5kaWNlcy5jbG9zaW5nX2luZGV4X3ZhbHVlIC0gbGFnKG5zZV9pbmRpY2VzLmNsb3NpbmdfaW5kZXhfdmFsdWUpIG92ZXIgKG9yZGVyIGJ5IHN5bWJvbCwgdHJhZGVfZGF0ZSkpOjpudW1lcmljLDIpIGFzIG5pZnR5Y2hhbmdlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUk9VTkQoKCgobnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZSkgLSBsYWcobnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZSkgb3ZlciAob3JkZXIgYnkgc3ltYm9sLCB0cmFkZV9kYXRlKSkvKGxhZyhuc2VfaW5kaWNlcy5jbG9zaW5nX2luZGV4X3ZhbHVlKSBvdmVyIChvcmRlciBieSBzeW1ib2wsIHRyYWRlX2RhdGUpKSoxMDApOjpudW1lcmljLDIpIGFzIG5pZnR5Y2hhbmdlX3BlcmNlbnQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyb20gbnNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbm5lciBqb2luIGRlbWF0IG9uIGRlbWF0LmlzaW4gPSBuc2UuaXNpbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVmdCBqb2luIG5zZV9pbmRpY2VzIG9uIG5zZV9pbmRpY2VzLmluZGV4X2RhdGUgPSBuc2UudHJhZGVfZGF0ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hlcmUgbnNlX2luZGljZXMuaW5kZXhfbmFtZSA9ICdOaWZ0eSA1MCcgYW5kIG5zZS50cmFkZV9kYXRlID49IFwnMjAxNy0wOS0wMVwnDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCBieSBuc2UudHJhZGVfZGF0ZSwgbnNlLnN5bWJvbCwgbnNlLmNsb3NlLCBkZW1hdC5kcF9iYWwsbnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgYnkgbnNlLnRyYWRlX2RhdGUgZGVzYywgbnNlLnN5bWJvbCIpDQoNCg0KcG9ydGZvbGlvX2dhaW5zIDwtIGRyb3BfbmEocG9ydGZvbGlvX2dhaW5zKQ0KDQoNCg0KIyBmb3IoaSBpbiBzZXFfYWxvbmcocG9ydGZvbGlvX2dhaW5zJGRheXNfZ2FpbikpDQojIHsNCiMgICBpZihwb3J0Zm9saW9fZ2FpbnMkZGF5c19nYWluW2ldIDw9IDApew0KIyAgICAgcG9ydGZvbGlvX2dhaW5zJGdyb3d0aFR5cGVbaV0gPC0gIk5lZ2F0aXZlIg0KIyAgIH1lbHNlew0KIyAgICAgcG9ydGZvbGlvX2dhaW5zJGdyb3d0aFR5cGVbaV0gPC0gIlBvc2l0aXZlIg0KIyAgIH0NCiMgfQ0KDQpwb3J0Zm9saW9fZ2FpbnMkZ3Jvd3RoVHlwZSA8LSBzYXBwbHkocG9ydGZvbGlvX2dhaW5zJGRheXNfZ2FpbiwgZnVuY3Rpb24oeCkgaWYoeDw9MCl7Ik5lZ2F0aXZlIn1lbHNleyJQb3NpdGl2ZSJ9KQ0KDQoNCnBvc19uZWcgPC0gcG9ydGZvbGlvX2dhaW5zJT4lc2VsZWN0KHN5bWJvbCwgZ3Jvd3RoVHlwZSklPiUNCiAgZ3JvdXBfYnkoc3ltYm9sLGdyb3d0aFR5cGUpJT4lDQogIHN1bW1hcmlzZShjbnQ9bigpKSU+JQ0KICBhcnJhbmdlKGdyb3d0aFR5cGUsLWNudCkNCg0KDQpwb3NfbmVnX3AgPC0gZ2dwbG90KHBvc19uZWdbd2hpY2gocG9zX25lZyRncm93dGhUeXBlPT0iTmVnYXRpdmUiKSxdLCBhZXMoeD1yZW9yZGVyKHN5bWJvbCwgY250KSwgeT1jbnQsIGZpbGw9ImxpZ2h0cmVkIikpKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKw0KICBjb29yZF9mbGlwKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrDQogIGdndGl0bGUoIk5vLiBvZiBkYXlzIFN0b2NrIGdhdmUgbmVnYXRpdmUgcmV0dXJucyIpDQoNCmdncGxvdGx5KHBvc19uZWdfcCwgaGVpZ2h0ID0gNTAwLCB3aWR0aCA9IDkwMCkNCg0KDQojIyBMZXRzIHBsb3QgdmlvbGluIHdpdGggc3RkIGFuZCBtZWFuDQoNCg0KDQoNCg0KIyNjYWxjdWxhdGUgcG9zaXRpdmUgbmVnYXRpdmUgZHJhZw0KDQpwX25fZHJhZyA8LSBwb3J0Zm9saW9fZ2FpbnMlPiVzZWxlY3Qoc3ltYm9sLCBkYXlzX2dhaW5fcGVyY2VudCwgZ3Jvd3RoVHlwZSklPiUNCiAgZ3JvdXBfYnkoc3ltYm9sLGdyb3d0aFR5cGUpJT4lDQogIGFycmFuZ2UoZGVzYyhkYXlzX2dhaW5fcGVyY2VudCkpJT4lDQogIHNsaWNlKDI6bigpKSU+JSAgICMjIGRyb3BwaW5nIGZpcnN0IHJvdyBhcyBpdCBtaWdodCBjb250YWluIG91dGxpZXJzIGR1ZSB0byBub24gYXZhaWxhYmlsaXR5IG9mIGRhdGEgb24gZWFybGllciBkYXRlcw0KICBzdW1tYXJpc2UodG90YWw9c3VtKGRheXNfZ2Fpbl9wZXJjZW50KSkNCiAgDQoNCiAgICANCnBfbl9kcmFnJHRvdGFsIDwtIGFicyhwX25fZHJhZyR0b3RhbCkNCg0KcF9uX2RyYWcgPC0gcF9uX2RyYWclPiVhcnJhbmdlKGRlc2MoZ3Jvd3RoVHlwZSksIGRlc2ModG90YWwpKQ0KDQoNCnBfbl9kcmFnX2NoYXJ0IDwtIGdncGxvdChwX25fZHJhZywgYWVzKHg9cmVvcmRlcihzeW1ib2wsIC10b3RhbCksIHk9dG90YWwsIGZpbGw9Z3Jvd3RoVHlwZSkpKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiKSsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHg9IiIseT0iIix0aXRsZT0iUG9zaXRpdmUgYW5kIE5lZ2F0aXZlIERyYWciKQ0KDQpnZ3Bsb3RseShwX25fZHJhZ19jaGFydCwgaGVpZ2h0ID0gNjAwLCB3aWR0aCA9IDExMDApDQoNCg0KIyMgQ2hlY2sgY3VscHJpdHMgZHJhZ2dpbmcgcG9ydGZvbGlvIGV2ZW4gd2hlbiBuaWZ0eSBjaGFuZ2UgaXMgcG9zaXRpdmUNCg0KY3VscHJpdF9zdG9ja3MgPC0gcG9ydGZvbGlvX2dhaW5zW3doaWNoKHBvcnRmb2xpb19nYWlucyRkYXlzX2dhaW4gPCAwICYgcG9ydGZvbGlvX2dhaW5zJG5pZnR5Y2hhbmdlID4gMCksXSANCg0KY3VscHJpdF9zdG9ja3Nfc3RhdHMgPC0gY3VscHJpdF9zdG9ja3MlPiVzZWxlY3Qoc3ltYm9sLCBkYXlzX2dhaW5fcGVyY2VudCwgb3ZlcmFsbGdhaW4pJT4lDQogIGdyb3VwX2J5KHN5bWJvbCklPiUNCiAgc3VtbWFyaXNlKHRpbWVzX2RlZmF1bHRlZF9uaWZ0eT1uKCksbG9zc1BlcmNlbnRfZHVyaW5nX2RlZmF1bHQ9c3VtKGRheXNfZ2Fpbl9wZXJjZW50KSwgbG9zc19kdXJpbmdfZGVmYXVsdCA9IHN1bShvdmVyYWxsZ2FpblsxXSkpDQoNCg0KDQpnZ3Bsb3QoY3VscHJpdF9zdG9ja3MsIGFlcyh4PXN5bWJvbCkpKw0KICBnZW9tX2JhcigpKw0KICBjb29yZF9mbGlwKCkNCg0KDQoNCg0KIyMgRGFpbHkgcG9ydGZvbGlvIHZhbHVlLCBwcm9maXQgJSBhbmQgTmlmdHkgNTAgY2xvc2luZyBpbmRleA0KDQpkYXRhNSA8LSBkYkdldFF1ZXJ5KGNuMSwgJ3NlbGVjdCBuc2UudHJhZGVfZGF0ZSwgc3VtKGRlbWF0LmRwX2JhbCAqIG5zZS5jbG9zZSkgYXMgcG9ydGZvbGlvVmFsdWUsIFJPVU5EKChzdW0oZGVtYXQuZHBfYmFsICogbnNlLmNsb3NlKSAtIHN1bShkZW1hdC5ob2xkX3ZhbHVlKSk6Om51bWVyaWMsMikgYXMgcHJvZml0LCBST1VORCgoKHN1bShkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKS9zdW0oZGVtYXQuaG9sZF92YWx1ZSkqMTAwKTo6bnVtZXJpYywyKSBhcyBwcm9maXRfcGVyY2VudCwgbnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZSBmcm9tIGRlbWF0DQogICAgICAgICAgICAgICAgICAgIGlubmVyIGpvaW4gbnNlIG9uIGRlbWF0LmlzaW4gPSBuc2UuaXNpbg0KICAgICAgICAgICAgICAgICAgICBsZWZ0IGpvaW4gbnNlX2luZGljZXMgb24gbnNlX2luZGljZXMuaW5kZXhfZGF0ZSA9IG5zZS50cmFkZV9kYXRlDQogICAgICAgICAgICAgICAgICAgIHdoZXJlIG5zZV9pbmRpY2VzLmluZGV4X25hbWUgPSBcJ05pZnR5IDUwXCcgYW5kIG5zZS50cmFkZV9kYXRlID49IFwnMjAxNy0wOC0wMVwnDQogICAgICAgICAgICAgICAgICAgIGdyb3VwIGJ5IG5zZS50cmFkZV9kYXRlLCBuc2VfaW5kaWNlcy5pbmRleF9uYW1lLCBuc2VfaW5kaWNlcy5jbG9zaW5nX2luZGV4X3ZhbHVlDQogICAgICAgICAgICAgICAgICAgIG9yZGVyIGJ5IG5zZS50cmFkZV9kYXRlIGRlc2MnKQ0KDQpnZ3Bsb3QoZGF0YTUsIGFlcyh4PXRyYWRlX2RhdGUsIHk9cHJvZml0X3BlcmNlbnQpKSsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdhdXRvJykrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGNvbG9yID0gJ3JlZCcsIGxpbmV0eXBlPSdkYXNoZWQnKSsNCiAgZ2VvbV9saW5lKGFlcyh5PWNsb3NpbmdfaW5kZXhfdmFsdWUvMTAwMCksIGNvbG9yPSdibHVlJykrDQogIGdndGl0bGUoJ1BvcnRmb2xpbyBQcm9maXQgJSB+IE5pZnR5IFRyZW5kJykNCg0KDQojQ2hlY2sgcG9ydGZvbGlvIGRheXMgZ2FpbiAlIGFnYWluc3QgTmlmdHkgZ2FpbiAlDQoNCmRhdGE2IDwtIGRiR2V0UXVlcnkoY24xLCAnc2VsZWN0IG5zZS50cmFkZV9kYXRlLCBzdW0oZGVtYXQuZHBfYmFsICogbnNlLmNsb3NlKSBhcyBwb3J0Zm9saW9WYWx1ZSwoUk9VTkQoKHN1bShkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKTo6bnVtZXJpYywyKSkgLSBsYWcoUk9VTkQoKHN1bShkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKTo6bnVtZXJpYywyKSkgb3ZlciAob3JkZXIgYnkgdHJhZGVfZGF0ZSkgYXMgZGF5c19nYWluLFJPVU5EKCgoUk9VTkQoKHN1bShkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKTo6bnVtZXJpYywyKSkgLSBsYWcoUk9VTkQoKHN1bShkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKTo6bnVtZXJpYywyKSkgb3ZlciAob3JkZXIgYnkgdHJhZGVfZGF0ZSkpKjEwMC8oc3VtKGRlbWF0LmhvbGRfdmFsdWUpKTo6bnVtZXJpYywyKSBhcyBkYXlzX2dhaW5fcGVyY2VudCxST1VORCgoc3VtKGRlbWF0LmRwX2JhbCAqIG5zZS5jbG9zZSkgLSBzdW0oZGVtYXQuaG9sZF92YWx1ZSkpOjpudW1lcmljLDIpIGFzIG92ZXJhbGxfZ2FpbixST1VORCgoKHN1bShkZW1hdC5kcF9iYWwgKiBuc2UuY2xvc2UpIC0gc3VtKGRlbWF0LmhvbGRfdmFsdWUpKS9zdW0oZGVtYXQuaG9sZF92YWx1ZSkqMTAwKTo6bnVtZXJpYywyKSBhcyBvdmVyYWxsX2dhaW5fcGVyY2VudCwgbnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZSBhcyBuaWZ0eSwgUk9VTkQoKCgobnNlX2luZGljZXMuY2xvc2luZ19pbmRleF92YWx1ZSAtIGxhZyhuc2VfaW5kaWNlcy5jbG9zaW5nX2luZGV4X3ZhbHVlKSBvdmVyIChvcmRlciBieSBuc2UudHJhZGVfZGF0ZSkpL25zZV9pbmRpY2VzLmNsb3NpbmdfaW5kZXhfdmFsdWUpKjEwMCk6Om51bWVyaWMsMikgYXMgbmlmdHlfY2hhbmdlX3BlcmNlbnQgIGZyb20gZGVtYXQNCiAgICAgICAgICAgICAgICAgICAgaW5uZXIgam9pbiBuc2Ugb24gZGVtYXQuaXNpbiA9IG5zZS5pc2luDQogICAgICAgICAgICAgICAgICAgIGxlZnQgam9pbiBuc2VfaW5kaWNlcyBvbiBuc2VfaW5kaWNlcy5pbmRleF9kYXRlID0gbnNlLnRyYWRlX2RhdGUNCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAgYnkgbnNlLnRyYWRlX2RhdGUsIG5zZV9pbmRpY2VzLmluZGV4X25hbWUsIG5zZV9pbmRpY2VzLmNsb3NpbmdfaW5kZXhfdmFsdWUNCiAgICAgICAgICAgICAgICAgICAgaGF2aW5nIG5zZV9pbmRpY2VzLmluZGV4X25hbWUgPSBcJ05pZnR5IDUwXCcgYW5kIG5zZS50cmFkZV9kYXRlID4gXCcyMDE3LTA4LTAxXCcNCiAgICAgICAgICAgICAgICAgICAgb3JkZXIgYnkgbnNlLnRyYWRlX2RhdGUgZGVzYycpDQoNCmRhdGE2W2lzLm5hKGRhdGE2KV08LSAwDQoNCnN1YjFfZGF0YTYgPC0gZGF0YTZbLGMoMSw0LDgpXQ0Kc3ViMV9kYXRhNiA8LSBzdWIxX2RhdGE2JT4lDQogIGFycmFuZ2UodHJhZGVfZGF0ZSklPiUNCiAgbXV0YXRlKFBvcnRmb2xpb19HYWluX1BlcmNlbnQgPSBjdW1zdW0oZGF5c19nYWluX3BlcmNlbnQpLCBOaWZ0eV9HYWluX1BlcmNlbnQgPSBjdW1zdW0obmlmdHlfY2hhbmdlX3BlcmNlbnQpKSU+JQ0KICBzZWxlY3QodHJhZGVfZGF0ZSwgUG9ydGZvbGlvX0dhaW5fUGVyY2VudCwgTmlmdHlfR2Fpbl9QZXJjZW50KQ0KDQpzdWIxX2RhdGE2IDwtIG1lbHQoc3ViMV9kYXRhNiwgaWQ9YygidHJhZGVfZGF0ZSIpKQ0KDQoNCg0KcDY8LWdncGxvdChzdWIxX2RhdGE2LCBhZXMoeD10cmFkZV9kYXRlLCB5PXZhbHVlLCBjb2xvcj12YXJpYWJsZSkpKw0KICBnZW9tX2xpbmUoc2l6ZT0wLjcpKw0KICB4bGFiKCJUcmFkZSBEYXRlIikrDQogIHlsYWIoIkdhaW4gJSIpKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViICV5IikrDQogIGdndGl0bGUoIkRhaWx5IFBvcnRmb2xpbyBHYWluICUgfiBOaWZ0eSBDaGFuZ2UgJSIpKw0KICB0aGVtZV9lY29ub21pc3QoKQ0KDQoNCg0KZ2dwbG90bHkocDYsIHdpZHRoID0gMTAwMCwgaGVpZ2h0ID0gNDAwKQ0KIyMjIEFuYWx5c2luZyBjdXJyZW50IHRyZW5kcw0KDQoNCm5zZSA8LSBkYkdldFF1ZXJ5KGNuMSwnc2VsZWN0IG5zZS50cmFkZV9kYXRlLCBuc2Uuc3ltYm9sLCBuc2UuaXNpbiwgbnNlLmNsb3NlLCBpbmR1c3RyeWNsYXNzLnNlY3Rvcl9uYW1lLCBpbmR1c3RyeWNsYXNzLmluZHVzdHJ5X25hbWUgIGZyb20gbnNlDQogICAgICAgICAgICAgICAgICBsZWZ0IGpvaW4gc2NyaXBzIG9uIG5zZS5pc2luID0gc2NyaXBzLmlzaW4NCiAgICAgICAgICAgICAgICAgIGxlZnQgam9pbiBpbmR1c3RyeWNsYXNzIG9uIGluZHVzdHJ5Y2xhc3MuaW5kdXN0cnlfc3ViZ3JvdXAgPSBzY3JpcHMuaW5kdXN0cnkNCiAgICAgICAgICAgICAgICAgIGdyb3VwIGJ5IG5zZS5zeW1ib2wsIG5zZS5pc2luLCBuc2UudHJhZGVfZGF0ZSwgbnNlLmNsb3NlLCBpbmR1c3RyeWNsYXNzLnNlY3Rvcl9uYW1lLCBpbmR1c3RyeWNsYXNzLmluZHVzdHJ5X25hbWUNCiAgICAgICAgICAgICAgICAgIG9yZGVyIGJ5IG5zZS50cmFkZV9kYXRlIGRlc2MsIG5zZS5zeW1ib2wnKQ0KDQpuc2VfZGF5cyA8LSBkYkdldFF1ZXJ5KGNuMSwnc2VsZWN0IHRyYWRlX2RhdGUgZnJvbSBuc2UgZ3JvdXAgYnkgdHJhZGVfZGF0ZSBvcmRlciBieSB0cmFkZV9kYXRlIGRlc2MnKQ0KDQpkMSA8LSBuc2VbbnNlJHRyYWRlX2RhdGUgJWluJSBuc2VfZGF5c1sxLDFdLF0NCmQ2MCA8LSBuc2VbbnNlJHRyYWRlX2RhdGUgJWluJSBuc2VfZGF5c1s2MCwxXSxdDQpkMTIwIDwtIG5zZVtuc2UkdHJhZGVfZGF0ZSAlaW4lIG5zZV9kYXlzWzEyMCwxXSxdDQpkMTgwIDwtIG5zZVtuc2UkdHJhZGVfZGF0ZSAlaW4lIG5zZV9kYXlzWzE4MCwxXSxdDQpkMjQwIDwtIG5zZVtuc2UkdHJhZGVfZGF0ZSAlaW4lIG5zZV9kYXlzWzI0MCwxXSxdDQpkMzAwIDwtIG5zZVtuc2UkdHJhZGVfZGF0ZSAlaW4lIG5zZV9kYXlzWzMwMCwxXSxdDQpkMzYwIDwtIG5zZVtuc2UkdHJhZGVfZGF0ZSAlaW4lIG5zZV9kYXlzWzM2MCwxXSxdDQoNCnRyZW5kX2RhdGEgPC0gZDENCnRyZW5kX2RhdGEkZDYwIDwtIGQ2MFttYXRjaCh0cmVuZF9kYXRhJGlzaW4sIGQ2MCRpc2luKSwnY2xvc2UnXQ0KdHJlbmRfZGF0YSRkMTIwIDwtIGQxMjBbbWF0Y2godHJlbmRfZGF0YSRpc2luLCBkMTIwJGlzaW4pLCdjbG9zZSddDQp0cmVuZF9kYXRhJGQxODAgPC0gZDE4MFttYXRjaCh0cmVuZF9kYXRhJGlzaW4sIGQxODAkaXNpbiksJ2Nsb3NlJ10NCnRyZW5kX2RhdGEkZDI0MCA8LSBkMjQwW21hdGNoKHRyZW5kX2RhdGEkaXNpbiwgZDI0MCRpc2luKSwnY2xvc2UnXQ0KdHJlbmRfZGF0YSRkMzAwIDwtIGQzMDBbbWF0Y2godHJlbmRfZGF0YSRpc2luLCBkMzAwJGlzaW4pLCdjbG9zZSddDQp0cmVuZF9kYXRhJGQzNjAgPC0gZDM2MFttYXRjaCh0cmVuZF9kYXRhJGlzaW4sIGQzNjAkaXNpbiksJ2Nsb3NlJ10NCg0KdHJlbmRfZGF0YSA8LSB0cmVuZF9kYXRhW2NvbXBsZXRlLmNhc2VzKHRyZW5kX2RhdGEpLF0NCg0KdHJlbmRfZGF0YSRkNjBfcHJjdCA8LSByb3VuZCgodHJlbmRfZGF0YSRjbG9zZSAtIHRyZW5kX2RhdGEkZDYwKS90cmVuZF9kYXRhJGNsb3NlICogMTAwLDIpDQp0cmVuZF9kYXRhJGQxMjBfcHJjdCA8LSByb3VuZCgodHJlbmRfZGF0YSRjbG9zZSAtIHRyZW5kX2RhdGEkZDEyMCkvdHJlbmRfZGF0YSRjbG9zZSAqIDEwMCwyKQ0KdHJlbmRfZGF0YSRkMTgwX3ByY3QgPC0gcm91bmQoKHRyZW5kX2RhdGEkY2xvc2UgLSB0cmVuZF9kYXRhJGQxODApL3RyZW5kX2RhdGEkY2xvc2UgKiAxMDAsMikNCnRyZW5kX2RhdGEkZDI0MF9wcmN0IDwtIHJvdW5kKCh0cmVuZF9kYXRhJGNsb3NlIC0gdHJlbmRfZGF0YSRkMjQwKS90cmVuZF9kYXRhJGNsb3NlICogMTAwLDIpDQp0cmVuZF9kYXRhJGQzMDBfcHJjdCA8LSByb3VuZCgodHJlbmRfZGF0YSRjbG9zZSAtIHRyZW5kX2RhdGEkZDMwMCkvdHJlbmRfZGF0YSRjbG9zZSAqIDEwMCwyKQ0KdHJlbmRfZGF0YSRkMzYwX3ByY3QgPC0gcm91bmQoKHRyZW5kX2RhdGEkY2xvc2UgLSB0cmVuZF9kYXRhJGQzNjApL3RyZW5kX2RhdGEkY2xvc2UgKiAxMDAsMikNCg0KZDM2MF90b3AxMCA8LSB0b3Bfbih0cmVuZF9kYXRhLCAxMCwgd3QgPSB0cmVuZF9kYXRhJGQzNjBfcHJjdCkNCmQ2MF90b3AxMCA8LSB0b3Bfbih0cmVuZF9kYXRhLCAxMCwgd3QgPSB0cmVuZF9kYXRhJGQ2MF9wcmN0KQ0KDQpwNyA8LSBnZ3Bsb3QoZDM2MF90b3AxMCwgYWVzKHg9cmVvcmRlcihzeW1ib2wsZDM2MF9wcmN0KSwgeT1kMzYwX3ByY3QsIGZpbGw9c3ltYm9sKSkrDQogIGdlb21fY29sKCkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgZ2d0aXRsZSgiVG9wIDEwIFBlcmZvcm1lcnMgLSBMb25nIFRlcm0gMzYwKyBUcmFkZXMiKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpDQoNCmdncGxvdGx5KHA3LCBoZWlnaHQgPSA0MDAsIHdpZHRoID0gODAwKQ0KDQoNCkxUX1RvcDEwX1RyZW5kIDwtIGdncGxvdChuc2VbbnNlJGlzaW4gJWluJSBkMzYwX3RvcDEwJGlzaW4sXSwgYWVzKHg9dHJhZGVfZGF0ZSwgeT1jbG9zZSkpKw0KICBnZW9tX2xpbmUoKSsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2F1dG8nKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpKw0KICBmYWNldF93cmFwKCdzeW1ib2wnLCBzY2FsZXMgPSAiZnJlZV95IikrDQogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiViICV5IikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gLTMwKSkrDQogIGdndGl0bGUoIlRSRU5EIC0gVG9wIDEwIFBlcmZvcm1lcnMgLSBMb25nIFRlcm0gMzYwKyBUcmFkZXMiKQ0KDQpnZ3Bsb3RseShMVF9Ub3AxMF9UcmVuZCwgd2lkdGg9MTAwMCwgaGVpZ2h0PTYwMCkNCg0KDQpTVF90b3AxMF9wIDwtIGdncGxvdChkNjBfdG9wMTAsIGFlcyh4PXJlb3JkZXIoc3ltYm9sLGQ2MF9wcmN0KSwgeT1kNjBfcHJjdCwgZmlsbD1zeW1ib2wpKSsNCiAgZ2VvbV9jb2woKSsNCiAgY29vcmRfZmxpcCgpKw0KICBnZ3RpdGxlKCJUb3AgMTAgUGVyZm9ybWVycyAtIFNob3J0IFRlcm0gNjArIFRyYWRlcyIpKw0KICB4bGFiKCIiKSsNCiAgeWxhYigiIikNCg0KZ2dwbG90bHkoU1RfdG9wMTBfcCwgaGVpZ2h0ID0gNDAwLCB3aWR0aCA9IDgwMCkNCg0KDQoNClNUX3RvcDEwX3BfdHJlbmQgPC0gZ2dwbG90KG5zZVtuc2UkaXNpbiAlaW4lIGQ2MF90b3AxMCRpc2luLF0sIGFlcyh4PXRyYWRlX2RhdGUsIHk9Y2xvc2UpKSsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdhdXRvJykrDQogIGZhY2V0X3dyYXAoJ3N5bWJvbCcsIHNjYWxlcyA9ICJmcmVlX3kiKSsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjMgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJXkiKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAtMzApKSsNCiAgZ2d0aXRsZSgnVFJFTkQgLSBUT1AgMTAgU2hvcnQgVGVybSA2MCsgdHJhZGVzJykNCg0KZ2dwbG90bHkoU1RfdG9wMTBfcF90cmVuZCwgd2lkdGg9MTAwMCwgaGVpZ2h0PTYwMCkNCg0KDQppbmR1c3RyaWVzX2Q2MF90b3A1IDwtIHRyZW5kX2RhdGEgJT4lIA0KICBhcnJhbmdlKGluZHVzdHJ5X25hbWUsIGRlc2MoZDYwX3ByY3QpKSAlPiUNCiAgZ3JvdXBfYnkoaW5kdXN0cnlfbmFtZSkgJT4lDQogIHRvcF9uKG49NSx3dCA9IGQ2MF9wcmN0KSAjICU+JQ0KI2ZpbHRlcihyb3dfbnVtYmVyKCkgPD01KQ0KDQoNCnBfc2hvcnRfdG9wNSA8LSBnZ3Bsb3QoaW5kdXN0cmllc19kNjBfdG9wNSwgYWVzKHg9cmVvcmRlcihzeW1ib2wsZDYwX3ByY3QpLCB5PWQ2MF9wcmN0KSkrDQogIGdlb21fY29sKCkrDQogIGNvb3JkX2ZsaXAoKSsNCiAgZmFjZXRfd3JhcCgnaW5kdXN0cnlfbmFtZScsIHNjYWxlcyA9ICdmcmVlX3knLCBuY29sID0gNCkrDQogIHhsYWIoIiIpKw0KICB5bGFiKCIiKSsNCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiwgZmFjZSA9ICJib2xkIikpKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNikpKw0KICB0aGVtZShwYW5lbC5zcGFjaW5nLnkgPSB1bml0KDAuNSwibGluZXMiKSkrDQogIHRoZW1lKHBhbmVsLnNwYWNpbmcueCA9IHVuaXQoMC41LCJsaW5lcyIpKSsNCiAgZ2d0aXRsZSgnU2hvcnQgVGVybSA2MCsgdHJhZGVzIHRvcHBlcnMgaW4gJSBnYWluJykNCg0KZ2dwbG90bHkocF9zaG9ydF90b3A1LCBoZWlnaHQgPSA4MDAsIHdpZHRoID0gMTAwMCkNCg0KDQpgYGA=